import { CommandFactory } from "../factory/CommandFactory";
import { RoleFactory } from "../factory/RoleFactory";
import { toMinute, toSecond } from "../tool";
import { Client } from "./Client";
import { Context } from "./Context";
import { CommandNotFindError, GameNotFindError, GameNotReadyError, RoleAlreadyExist, RoleDieError, RoleNotFindError } from "./errors";
import { GroupMessage } from "./Message";
import { Role, RoleStatus } from "./Role";

export enum GameStatus { "start", "stop", "ready" }
interface CheckRoles { id: string, userName: string, roleName: string }

/**
 * 游戏类
 */
export class Game {
    /** 所有的 game 对象 */
    static games: Game[] = [];
    /** 游戏的状态 */
    public status: GameStatus = GameStatus.stop;
    /** 角色对应表 */
    public roles: Role[] = [];
    /** 参加到这一局的待定人员 */
    public checkRoles: CheckRoles[] = [];
    /** 一局游戏的时间 */
    public startTimeLength: number = 20E3 * 60;
    /** 游戏开始的时间 */
    private startTime: number = 0;
    /** 游戏的准备时间 */
    public readyTimeLength: number = 2E3 * 60;
    /** 游戏的停止时间 */
    public stopTimeLength: number = 10E3;
    // 保存定时器的返回值，在game结束的时候清除
    private timeouts: { start?: NodeJS.Timeout, stop?: NodeJS.Timeout, ready?: NodeJS.Timeout } = {
        start: undefined,
        stop: undefined,
        ready: undefined,
    }
    public message: GroupMessage;

    static hasGame(groupId: string): boolean {
        return this.games.find(e => e.groupId == groupId) !== undefined;
    }

    /** 通过群号获取 game（不存在会抛出异常，可通过 hasGame 判断是否存在） */
    static getGame(groupId: string) {
        const game = this.games.find(e => e.groupId === groupId)
        if (game === undefined) {
            throw new GameNotFindError();
        } else {
            return game;
        }
    }

    static removeGame(groupId: string) {
        if (this.hasGame(groupId)) {
            const index = this.games.findIndex(e => e.groupId === groupId);
            this.games.splice(index, 1)
        } else {
            throw new GameNotFindError();
        }
    }

    /** 终止一个游戏 */
    static killGame(groupId: string) {
        const game = this.getGame(groupId);
        this.removeGame(groupId);
        game.close();
    }

    /**
     * 游戏分为准备、开始、停止 三个阶段循环
     */
    constructor(public client: Client, public groupId: string) {
        this.message = new GroupMessage(client, groupId);
        this.message.add("<游戏开始>")
        this.onReady();
        // 将自己添加到games中，方便查找
        (this.constructor as typeof Game).games.push(this);
    }
    onStart() {
        // 如果人数太少，不开始
        if (this.checkRoles.length <= 1) {
            this.message.add("游戏准备人数应不少于1人。")
            Game.killGame(this.groupId);
        } else {
            this.start();
            this.timeouts.start = setTimeout(() => this?.onStop(), this.startTimeLength)
            this.message.add(`游戏进入开始阶段，持续时间${toMinute(this.startTimeLength)}分钟。`)
        }
    }

    /** 游戏开始阶段 */
    start() {
        this.status = GameStatus.start;
        clearTimeout(this.timeouts.ready as unknown as number); // 清除准备的定时器
        // 设置游戏开始时间
        this.startTime = Date.now();

        // 将准备名单中的角色创建实例
        this.roles = this.checkRoles.map(e => {
            // 通过角色名获取这个角色类
            const roleClass = RoleFactory.getByName(e.roleName);
            // 创建这个角色的实例
            const role = new roleClass(e.id, e.userName, this);
            return role;
        });
        this.checkRoles = [];// 清空待准备表
    }
    /** 游戏停止阶段 */
    onStop() {
        const activeRoles = this.roles.filter(role => role.getStatus() == RoleStatus.Active)
        this.message.add(`恭喜获得胜利的玩家：${activeRoles.map(e => e.userName).join("、")}`)
        this.stop();
        this.timeouts.stop = setTimeout(() => this?.onReady(), this.stopTimeLength)
        this.message.add(`游戏进入停止阶段，持续时间${toSecond(this.stopTimeLength)}秒。`)

    }

    stop() {
        this.status = GameStatus.stop;
        clearTimeout(this.timeouts.start as unknown as number); // 清除开始的定时器
        this.roles.forEach(role => role.gameOverHook());// 触发游戏结束钩子，通知它结束了
        this.roles = [];// 清空所有角色
    }

    /** 游戏准备阶段 */
    onReady() {
        this.read();
        this.timeouts.ready = setTimeout(() => this?.onStart(), this.readyTimeLength)
        this.message.add(`游戏进入准备阶段，持续时间${toSecond(this.readyTimeLength)}秒。`)
    }
    read() {
        this.status = GameStatus.ready;
        clearTimeout(this.timeouts.stop as unknown as number); // 清除结束的定时器
    }

    /** 当角色阵亡会出发这个事件 */
    onDirRole(role: Role) {
        if (this.getActiveRoles().length <= 1) {
            this.onStop();//关闭游戏
        }
    }

    /** 关闭游戏 */
    close() {
        this.message.add("<游戏结束>")
        this.message.close(); // 关闭消息(内部有定时器在运行)
        // 清除所有定时器
        clearTimeout(this.timeouts.ready as unknown as number)
        clearTimeout(this.timeouts.start as unknown as number)
        clearTimeout(this.timeouts.stop as unknown as number)
        this.stop(); //关闭游戏(内部销毁所有角色)
    }


    /**
     * 通过id获取角色
     * @param id 角色ID
     * @returns {Role}
     * @throws {RoleNotFindError|RoleDieError}
     * @description 必要时，请捕获异常情况
     */
    public getRole(id: string): Role {
        const role = this.roles.find(e => e.userId == id);
        if (role === undefined) {
            throw new RoleNotFindError()
        }
        // 如果角色已经阵亡，就不允许进行操作了。
        if (role.getStatus() == RoleStatus.Die) {
            throw new RoleDieError()
        }
        return role;
    }

    /** 获取所有存活的角色 */
    public getActiveRoles() {
        return this.roles.filter(role => role.getStatus() == RoleStatus.Active)
    }

    /** 获取所有已经阵亡的角色 */
    public getDieRoles() {
        return this.roles.filter(role => role.getStatus() == RoleStatus.Die)
    }

    /** 获取所有角色信息 */
    public getRolesInfo() {
        return this.roles.map(role => {
            return `${role.userName}[${role.getRoleName()}](${role.getStatus()})`
        }).join("、")
    }

    /** 判断一个角色是否存在 */
    public hasRole(id: string) {
        return this.roles.find(e => e.userId == id) !== undefined
    }

    public addCheckRoles(checkRole: CheckRoles) {
        if (this.status !== GameStatus.ready) {
            throw new GameNotReadyError()
        }
        if (this.checkRoles.find(e => e.id === checkRole.id) !== undefined) {
            throw new RoleAlreadyExist();
        }
        if (RoleFactory.getByName(checkRole.roleName) === undefined) {
            throw new RoleNotFindError();
        }
        this.checkRoles.push(checkRole)
    }


    /** 返回游戏距离结束的时间（毫秒） */
    public getStartTime() {
        const time = this.startTime + this.startTimeLength - Date.now()
        return time <= 0 ? 0 : time;
    }

    /**
     * 执行文本命令
     * @param commandText 
     * @returns 
     */
    public excute(commandText: string) {
        const context = new Context(commandText);
        try {
            this.callCommand(context);
        } catch (e) {
            if (e instanceof CommandNotFindError) {
                // 群友发送一些非指令的东西，不管他
            } else if (e! instanceof Error) {
                this.message.add(e.message)
            } else {
                console.error(e);
            }
        }
    }

    public callCommand(context: Context) {
        const commandName = context.nextParam();

        const conmandClass = CommandFactory.getByName(commandName);
        const command = new conmandClass(this, context);
        return command.invoke();
    }
}